package fr.generali.sas.service.flux;

import java.util.Collections;
import java.util.List;

import javax.annotation.Resource;
import javax.xml.bind.JAXBElement;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import fr.generali.sas.dao.FluxInternalDao;
import fr.generali.sas.model.v1_0.Flux;
import fr.generali.sas.model.v1_0.InfoClientResponse;
import fr.generali.sas.model.v1_0.NotificationIntegrationResponseType;
import fr.generali.sas.model.v1_0.ObjectFactory;
import fr.generali.sas.model.v1_0.StatutType;
import fr.generali.sas.service.provider.SequenceProvider;

@Component("fluxSyncServiceSimple")
public class FluxSyncServiceSimpleImpl implements InitializingBean, InternalFluxSynchroService,FluxSynchroService {
	
	@Value("${row.limit}")
	int rowLimit = 100;
	
	@Resource
	FluxInternalDao dao;
	
	@Resource
	SequenceProvider sequenceProvider;
	
	ObjectFactory sasModelObjectFactory = new ObjectFactory();
	
	
	private static Logger logger = LoggerFactory.getLogger(FluxSyncServiceSimpleImpl.class);
	
	
	@Override
	public void afterPropertiesSet() throws Exception {
		Assert.notNull(dao, "dao should not be null");
		Assert.notNull(sequenceProvider,"sequenceProvider should not be null");
	}
	
	
	/**
	 * Genere un numero de resa 
	 * 
	 * Note : implementation a externaliser et à injecter => modification de l'impléementation plus souple
	 * 
	 * @param codeIntermediaire 
	 * @return
	 */
	private String genererNumeroReservation(String codeIntermediaire) {
		
		return codeIntermediaire+ sequenceProvider.getNextSequenceNumber();
		//return codeIntermediaire + new java.sql.Timestamp(new Date().getTime());
	}
	
	/**{@inheritDoc
	 * @see fr.generali.sas.service.flux.InternalFluxSynchroService#getFluxClient(java.lang.String, java.lang.String)
	 */
	@Override
	@Transactional
	public List<Flux> getFluxClient(String codeIntermediaire, String idClient) 	throws IllegalArgumentException, Exception {
		
		List<Flux> listFlux = Collections.emptyList();
		String numeroReservation;
		
		if(StringUtils.isBlank(codeIntermediaire) || StringUtils.isBlank(idClient)) {
			logger.debug("codeIntermediaire:" + codeIntermediaire +"\n  idClient:" + idClient);
			throw new IllegalArgumentException("neither codeIntermediaire nor idClient should be blank");
		}
		
		numeroReservation = genererNumeroReservation(codeIntermediaire);
		listFlux = getFluxClient(codeIntermediaire, idClient, numeroReservation);
		return listFlux;
	}

	

	
	/**{@inheritDoc}
	 * @see fr.generali.sas.service.flux.InternalFluxSynchroService#getFluxAgence(java.lang.String)
	 */
	public List<Flux> getFluxAgence(String codeIntermediaire) throws IllegalArgumentException, Exception {
		List<Flux> listFlux = Collections.emptyList();
		String numeroReservation;
		
		if(StringUtils.isBlank(codeIntermediaire) ) {
			logger.debug("codeIntermediaire:" + codeIntermediaire );
			throw new IllegalArgumentException("codeIntermediaire  should not be blank");
		}
		
		numeroReservation = genererNumeroReservation(codeIntermediaire);
		listFlux = getFluxAgence(codeIntermediaire, numeroReservation);
		return listFlux;
	}

	
	/**{@inheritDoc}
	 * @see fr.generali.sas.service.flux.InternalFluxSynchroService#acquitteSuccesIntegrationFlux(java.lang.String, java.lang.String)
	 */
	@Transactional
	public int acquitteSuccesIntegrationFlux(String codeIntermediaire,	String numeroReservation) throws IllegalArgumentException,	Exception {
		int updates = acquitteIntegrationFlux(codeIntermediaire, numeroReservation, true);
		
		return updates;
	}
	
	/**{@inheritDoc}
	 * @see fr.generali.sas.service.flux.InternalFluxSynchroService#acquitteEchecIntegrationFlux(java.lang.String, java.lang.String)
	 */
	@Transactional
	public int acquitteEchecIntegrationFlux(String codeIntermediaire, String numeroReservation) throws IllegalArgumentException, Exception {
		int updates = acquitteIntegrationFlux(codeIntermediaire, numeroReservation, false);
		
		return updates;
	}
	
	/**
	 * @param codeIntermediaire
	 * @param numeroReservation
	 * @return
	 */
	private int acquitteIntegrationFlux(String codeIntermediaire,
			String numeroReservation, boolean statut) {
		//il s'agit d'un acquittement en masse 
		//(tout est ok ou tout est ko) => aucun controle n'est effectué
		if(StringUtils.isBlank(codeIntermediaire) || StringUtils.isBlank(numeroReservation)) {
			logger.debug("codeIntermediaire:" + codeIntermediaire );
			throw new IllegalArgumentException("neither codeIntermediaire nor numeroReservation  should be blank");
		}		
		int updates = dao.acquitteIntegrationFlux(codeIntermediaire, numeroReservation, statut);
		return updates;
	}


	/**{@inheritDoc}
	 * @see fr.generali.sas.service.flux.InternalFluxSynchroService#getNextFluxClient(java.lang.String, java.lang.String, int)
	 */
	@Transactional(readOnly=true, propagation=Propagation.SUPPORTS)
	public List<Flux> getNextFluxClient(String codeIntermediaire, String numeroReservation, int fromId) {
		List<Flux> listFlux = Collections.emptyList();
		
		listFlux = dao.getNextFluxPourSynchroClientWithRowLimit(codeIntermediaire, numeroReservation, rowLimit, fromId);
		return listFlux;
	}


	/**{@inheritDoc}
	 * @see fr.generali.sas.service.flux.InternalFluxSynchroService#makeInfoClientResponseForClientRequest(java.lang.String, java.lang.String)
	 */
	public InfoClientResponse makeInfoClientResponseForClientRequest(String codeIntermediaire, String idClient) throws IllegalArgumentException, Exception {
		
		if(StringUtils.isBlank(codeIntermediaire) || StringUtils.isBlank(idClient)) {
			logger.debug("codeIntermediaire:" + codeIntermediaire +"\n  idClient:" + idClient);
			throw new IllegalArgumentException("neither codeIntermediaire nor idClient should be blank");
		}
		
		String numeroReservation = genererNumeroReservation(codeIntermediaire);
		List<Flux> listFluxs = getFluxClient(codeIntermediaire, idClient, numeroReservation);
		InfoClientResponse response = makeInfoClientResponse(codeIntermediaire,numeroReservation, listFluxs);

		return response;
	}
	
	
	/**
	 * @param codeIntermediaire
	 * @param idClient
	 * @param numeroReservation
	 * @return
	 * @throws IllegalArgumentException
	 * @throws Exception
	 */
	public List<Flux> getFluxClient(String codeIntermediaire, String idClient, String numeroReservation) throws IllegalArgumentException, Exception {
		List<Flux> listFlux = Collections.emptyList();
		
		//reserver les flux s'il y en a
		int nbFluxReserves = reserverFluxPourSynchronisationClient(codeIntermediaire, idClient, numeroReservation);
		if (nbFluxReserves > 0) {
			if (nbFluxReserves < rowLimit) {
				// recuperer tous les flux
				listFlux = dao.getFluxPourSynchroClient(codeIntermediaire,numeroReservation);
			} else {
				// recuperer tous les flux par paquet
				listFlux = dao.getFluxPourSynchroClientWithRowLimit(codeIntermediaire,numeroReservation,rowLimit);
			}
		}
		return listFlux;
	}


	/**
	 * @param codeIntermediaire
	 * @param idClient
	 * @param numeroReservation
	 * @return
	 */
	@Transactional
	public int reserverFluxPourSynchronisationClient(String codeIntermediaire, String idClient, String numeroReservation) {
		int nbFluxReserves = dao.reserveFluxPourSynchroClient(codeIntermediaire, numeroReservation, idClient);
		return nbFluxReserves;
	}
	
	/**
	 * @param codeIntermediaire
	 * @param numeroreservation
	 * @return
	 */
	@Transactional
	public List<Flux> getFluxAgence(String codeIntermediaire, String numeroReservation) {
		
		List<Flux> listFlux = Collections.emptyList();
		
		//reserver les flux s'il y en a
		int nbFluxReserves = dao.reserveFluxPourSynchroAgence(codeIntermediaire, numeroReservation);
		if (nbFluxReserves > 0) {
			if (nbFluxReserves < rowLimit) {
				// recuperer tous les flux
				listFlux = dao.getFluxPourSynchroAgence(codeIntermediaire,numeroReservation);
			} else {
				// recuperer tous les flux par paquet
				listFlux = dao.getFluxPourSynchroAgenceWithRowLimit(codeIntermediaire,numeroReservation,rowLimit);
			}
		}
		return listFlux;
	}


	/**{@inheritDoc}
	 * @see fr.generali.sas.service.flux.InternalFluxSynchroService#makeInfoClientResponseForAgenceRequest(java.lang.String)
	 */
	@Transactional
	public InfoClientResponse makeInfoClientResponseForAgenceRequest(String codeIntermediaire) throws IllegalArgumentException,	Exception {
		if(StringUtils.isBlank(codeIntermediaire) ) {
			logger.debug("codeIntermediaire:" + codeIntermediaire );
			throw new IllegalArgumentException("codeIntermediaire should not be blank");
		}
		
		String numeroReservation = genererNumeroReservation(codeIntermediaire);
		List<Flux> listFluxs = getFluxAgence(codeIntermediaire, numeroReservation);
		InfoClientResponse response = makeInfoClientResponse(codeIntermediaire,numeroReservation, listFluxs);

		return response;
	}


	/**
	 * @param codeIntermediaire TODO
	 * @param numeroReservation
	 * @param listFluxs
	 * @return
	 */
	private InfoClientResponse makeInfoClientResponse(String codeIntermediaire,	String numeroReservation, List<Flux> listFluxs) {
		
		int nbFluxReserves = 0;
		InfoClientResponse response = new InfoClientResponse();
		if(listFluxs.isEmpty()) {
			response.setDernierItem(null);
		}else {
			nbFluxReserves = dao.getNbFluxForReservation(codeIntermediaire, numeroReservation);
			response.setDernierItem(Integer.valueOf(listFluxs.get(listFluxs.size()-1).getId()));
		}
		
		response.setNbTotalFlux(nbFluxReserves);
		response.setNbFlux(listFluxs.size());
		response.setNumeroDemande(numeroReservation);
		response.getFluxes().addAll(listFluxs);
		return response;
	}


	/**{@inheritDoc}
	 * @see fr.generali.sas.service.flux.InternalFluxSynchroService#makeInfoClientResponseForPoursuivreRequest(java.lang.String, java.lang.String, int)
	 */
	public InfoClientResponse makeInfoClientResponseForPoursuivreRequest(
			String codeIntermediaire, String numeroReservation, int fromId)
			throws IllegalArgumentException, Exception {
		
		//faiblesse du design : trust le client pour le fromId. 
		//pas de trace de ce qui a été déjà envoyé (si client le demande c'est parce qu'il ne l'a pas recu => il faut donc l'envoyer
		if(StringUtils.isBlank(codeIntermediaire) || StringUtils.isBlank(numeroReservation) || fromId<0 ){
			logger.error("codeIntermediaire:" + codeIntermediaire  + " numeroReservation:" + numeroReservation + " fromId:"+fromId);
			throw new IllegalArgumentException("codeIntermediaire, numeroReservation should not be blank or fromIdmust be positive. \n Please check in parameters.");
		}
		
		List<Flux> listFluxs = getNextFluxClient(codeIntermediaire, numeroReservation, fromId);
		InfoClientResponse response = makeInfoClientResponse(codeIntermediaire,numeroReservation, listFluxs);
		
		return response;
	}


	
	/**{@inheritDoc}
	 * @see fr.generali.sas.service.flux.FluxSynchroService#makeRecupererAgenceResponse(java.lang.String)
	 */
	public JAXBElement<InfoClientResponse> makeRecupererAgenceResponse(	String codeIntermediaire) throws IllegalArgumentException,	Exception {
		
		InfoClientResponse response = this.makeInfoClientResponseForAgenceRequest(codeIntermediaire);
		JAXBElement<InfoClientResponse> element = this.sasModelObjectFactory.createRecupererAgenceResponse(response);
		
		return element;
	}


	
	/**{@inheritDoc}
	 * @see fr.generali.sas.service.flux.FluxSynchroService#makeRecupererClientResponse(java.lang.String, java.lang.String)
	 */
	public JAXBElement<InfoClientResponse> makeRecupererClientResponse(	String codeIntermediaire, String idClient) throws IllegalArgumentException, Exception {
		InfoClientResponse response = this.makeInfoClientResponseForClientRequest(codeIntermediaire, idClient);
		JAXBElement<InfoClientResponse> element = this.sasModelObjectFactory.createRecupererClientResponse(response);
		
		return element;
	}


	
	/**{@inheritDoc}
	 * @see fr.generali.sas.service.flux.FluxSynchroService#makePoursuivreResponse(java.lang.String, java.lang.String, java.lang.String)
	 */
	public JAXBElement<InfoClientResponse> makePoursuivreResponse(
			String codeIntermediaire, String numeroReservation, String fromId)
			throws IllegalArgumentException, Exception {
		
		InfoClientResponse response = this.makeInfoClientResponseForPoursuivreRequest(codeIntermediaire, numeroReservation, NumberUtils.toInt(fromId));
		JAXBElement<InfoClientResponse> element = this.sasModelObjectFactory.createPoursuivreResponse(response);
		
		return element;
	}


	
	
	/**{@inheritDoc}
	 * @see fr.generali.sas.service.flux.FluxSynchroService#makeNotificationIntegrationResponse(java.lang.String, java.lang.String, int)
	 */
	public JAXBElement<NotificationIntegrationResponseType> makeNotificationIntegrationResponse(String codeIntermediaire, 
																								String numeroReservation, 
																								int nbFlux, 
																								boolean success) throws IllegalArgumentException, Exception {
		NotificationIntegrationResponseType response = new NotificationIntegrationResponseType();
		response.setCodeIntermediaire(codeIntermediaire);
		response.setNumeroDemande(numeroReservation);
		response.setStatut(StatutType.OK);
		response.setNbFluxAck(nbFlux);
		JAXBElement<NotificationIntegrationResponseType> element = null;

		if (success) {
			element = this.sasModelObjectFactory.createNotificationIntegrationSuccesResponse(response);
		} else {
			element = this.sasModelObjectFactory.createNotificationIntegrationEchecResponse(response);

		}

		return element;
	}

	

}
